#include "cc_shell.h"
#include "cc_log.h"
#include "cc_mqtt.h"
#include "cc_msg.h"

int shell_current_msg_node;
cc_que_t shell_rx_queue;
cc_mutex_t shell_data_handle_lock;
uint8_t cc_shell_debug = 0;
commands_main commmand_init[CC_CONFIG_SHELL_COMMAND_MAX];
commands_main *__commmand_init_start = commmand_init;
commands_main *__commmand_init_end = commmand_init;
#define foreach_commmand(pos) \
  for (pos = __commmand_init_start; pos < __commmand_init_end; pos++)

void cc_shell_command_register(shell_cb func, const char *name,
                               const char *info)
{
  commands_main *entry = __commmand_init_end;
  entry->func = func;
  memcpy(entry->command, name, CC_CONFIG_SHELL_COMMAND_NAME_MAX);
  memcpy(entry->info, info, CC_CONFIG_SHELL_INFO_MAX);
  __commmand_init_end += 1;
  return;
}

int cc_shell_parse_args(const char *req_args, const char *req_expr, ...)
{
  va_list args;
  int req_args_num = 0;
  CC_ASSERT(req_args);
  CC_ASSERT(req_expr);
  va_start(args, req_expr);
  req_args_num = vsscanf(req_args, req_expr, args);
  va_end(args);
  return req_args_num;
}

static char result[2048] = ""; // 初始化结果数组
static void help(char *param1, int node)
{
  commands_main *ptr = __commmand_init_start;

  char temp[CC_CONFIG_SHELL_COMMAND_NAME_MAX + CC_CONFIG_SHELL_INFO_MAX + 20];
  strcpy(result, "\r\n-----command help-----\r\n");
  foreach_commmand(ptr)
  {
    sprintf(temp, "%s                      %s\r\n", ptr->command, ptr->info);
    if (strlen(result) + strlen(temp) > 2048)
    {
      cc_log_e("some cmd can not printf because do not have enough mem\r\n");
      break;
    }
    strcat(result, temp);
    memset(temp, 0, sizeof(temp));
  }
  strcat(result, "----------------------\r\n");

  // 输出最终的字符串
  cc_msg_resp(node, result, 0, 0);
}
CC_COMMAND_SHELL(help, "list of shell commands")

int cc_shell_init()
{
  shell_rx_queue =
      cc_queue_create(CC_CONFIG_SHELL_RX_QUEUE_LEN, sizeof(command_pack));
  shell_data_handle_lock = cc_mutex_create();
  return 1;
}
CC_INIT_APP_REGISTER(cc_shell_init)

static command_pack *get_command_pack(char *cmd, char *param, int node)
{
  command_pack *pack = (command_pack *)cc_malloc(sizeof(command_pack));
  if (pack == CC_NULL)
  {
    cc_log_e("oom\r\n");
    return CC_NULL;
  }
  memcpy(pack->command, cmd, strlen(cmd) + 1);
  memcpy(pack->param, param, strlen(param) + 1);
  pack->node = node;
  return pack;
}
void cc_shell_data_handle(cc_mnode_t data_node)
{
  if (cc_mutex_lock_time(shell_data_handle_lock, 500) == cc_false)
  {
    cc_log_e("failed get lock\r\n");
    return;
  }
  char command[CC_CONFIG_SHELL_COMMAND_NAME_MAX];
  char param[CC_CONFIG_SHELL_PARAM_MAX_LEN];
  memset(command, 0, sizeof(command));
  memset(param, 0, sizeof(param));
  cc_msg_frame *frame;
  frame = (cc_msg_frame *)data_node->mem;
  uint8_t shell_head_offset = 6;
  char *data = frame->data;
  if (cc_shell_debug == 1)
    cc_log("recv raw data:%s\r\n", data);
  data += 6; // 还要偏移一个空格

  int cnt = 0;
  while (data[0] != ' ' && data[0] != '\0')
  {
    command[cnt++] = data[0];
    if (cnt >= CC_CONFIG_SHELL_COMMAND_NAME_MAX)
    {
      cc_log_e("cmd to long\r\n");
      cc_mutex_unlock(shell_data_handle_lock);
      return;
    }
    data++;
  }
  if (cc_shell_debug)
  {
    cc_log("recv param cmd:%s\r\n", command);
  }
  // 跨过逗号
  data++;
  if (cc_shell_debug)
  {
    cc_log("recv param data:%s\r\n", data);
  }
  if (strlen(data) >= CC_CONFIG_SHELL_PARAM_MAX_LEN)
  {
    cc_log_e("param to long\r\n");
    cc_mutex_unlock(shell_data_handle_lock);
    return;
  }

  memcpy(param, data, strlen(data));

  // 向控制台线程发送指令
  command_pack *pack = get_command_pack(command, param, frame->node);
  if (pack == CC_NULL)
  {
    cc_mutex_unlock(shell_data_handle_lock);
    return;
  }
  cc_queue_send(shell_rx_queue, &pack);
  cc_mutex_unlock(shell_data_handle_lock);
}

static void cc_shell_handle(command_pack *pack)
{
  commands_main *ptr = __commmand_init_start;
  uint8_t flag = 0;
  foreach_commmand(ptr)
  {
    if (strcmp(ptr->command, pack->command) == 0)
    {
      flag = 1;
      ptr->func(pack->param, pack->node);
      if (cc_shell_debug)
        cc_log("exe success\r\n");
    }
  }
  if (flag != 1)
  {
    cc_log_e("not support the cmd(%s),use help to get support cmd\r\n or use "
             "CC_COMMAND_SHELL to add the cmd(%s)",
             pack->command, pack->command);
  }
  ptr = __commmand_init_start;
}

int cc_is_all_digits(const char *str)
{
  int len = strlen(str);
  for (size_t i = 0; i < len; i++)
  {
    if (!isdigit((uint8_t)str[i]))
    {
      return false;
    }
  }

  return cc_true;
}

void cc_shell_entry(void *data)
{
  command_pack *pack;
  while (1)
  {
    if (cc_queue_recv_time(shell_rx_queue, &pack, 100))
    {
      if (cc_shell_debug == 1)
        cc_log("recv commmad:%s param:%s\r\n", pack->command, pack->param);
      cc_shell_handle(pack);
      free(pack);
    }
  }
}
CC_THREAD_REGISTER_ALIAS(cc_shell_entry, 30, 3072, "shell_entry")

#if (LOG_D == 1)
void shell_debug(char *param1, int node)
{
  cc_log_d("recv from node(%d):%s\r\n", node, param1);
  if (cc_is_all_digits(param1))
  {
    cc_shell_debug = atoi(param1);
    if (cc_shell_debug < 0 || cc_shell_debug > 1)
    {
      cc_log_e("need 1 or 0\r\n");
      return;
    }
  }
  else
  {
    cc_log_e("need all digit param\r\n");
    return;
  }

  if (cc_shell_debug)
  {
    cc_msg_resp(node, "serial debug open\r\n", 0, CC_NULL);
  }
  else
  {
    cc_msg_resp(node, "serial debug close\r\n", 0, CC_NULL);
  }
}
CC_COMMAND_SHELL(shell_debug, "enable(1) disable(0) shell debug");
#endif
